package com.frinder.official.akka.fault;

import akka.actor.OneForOneStrategy;
import akka.actor.Props;
import akka.actor.SupervisorStrategy;
import akka.japi.pf.DeciderBuilder;
import com.frinder.official.spring.MyBaseActor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import scala.concurrent.duration.Duration;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

import static akka.actor.SupervisorStrategy.*;

/**
 * Created by frinder on 2017/9/13.
 * <p>
 * The supervisor itself is supervised by the top-level actor provided by the ActorSystem,
 * which has the default policy to restart in case of all Exception cases (with the notable exceptions of ActorInitializationException and ActorKilledException).
 * Since the default directive in case of a restart is to kill all children,
 * we expected our poor child not to survive this failure.
 * <p>
 * In case this is not desired (which depends on the use case),
 * we need to use a different supervisor which overrides this behavior.
 */
@Component
@Scope("prototype")
public class Supervisor2 extends MyBaseActor {

    private static SupervisorStrategy strategy =
            new OneForOneStrategy(10, Duration.create(1, TimeUnit.MINUTES), DeciderBuilder.
                    match(ArithmeticException.class, e -> resume()).
                    match(NullPointerException.class, e -> restart()).
                    match(IllegalArgumentException.class, e -> stop()).
                    matchAny(o -> escalate()).build());

    @Override
    public SupervisorStrategy supervisorStrategy() {
        return strategy;
    }

    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .match(Props.class, props -> {
                    log.debug("Props {}", props);
                    getSender().tell(getContext().actorOf(props), getSelf());
                })
                .build();
    }

    @Override
    public void preRestart(Throwable cause, Optional<Object> msg) {
        // do not kill all children, which is the default here
    }
}